package tuvienna.jade.table;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.TickerBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.util.Logger;

import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import tuvienna.jade.CentralRandom;
import tuvienna.jade.Constants;
import tuvienna.jade.InterestSet;

/**
 * An agent playing the role of a table on a conference.
 * Participants can register to a table and deregister from it.
 *
 */
public class TableAgent extends Agent {

	private static final long serialVersionUID = 8429252162306590026L;

	private Map<AID, InterestSet> participants = new TreeMap<AID, InterestSet>();
	
	private Logger myLogger = Logger.getMyLogger(getClass().getName());

	transient protected TableAgentGui gui;  // this is the gui
	  
	
	boolean hasGui = false;
	boolean lastParticipantCanDeregister = false;

	public int discussionTickerTime = 2000;
	protected void setup() {
		
		Object[] args = getArguments();
		if (args != null) { 
			int cur = -1;
			while(++cur < args.length) {
				if("-discussionTickerTime".equals(args[cur]) &&
				   ++cur < args.length)
					try	{	
						this.discussionTickerTime = Integer.parseInt(args[cur].toString());
					} catch(NumberFormatException e)	{
						System.out.println("Parsing timeDisountFactor failed. \n " +e.getMessage());
					}
				
				if("-gui".equals(args[cur]) &&
						++cur < args.length)
						try	{	
							this.hasGui = Boolean.parseBoolean(args[cur].toString());
						}
						catch(NumberFormatException e)	{
							System.out.println("Parsing gui failed. \n " +e.getMessage());
						}
				if("-lastParticipantCanDeregister".equals(args[cur]) &&
						++cur < args.length)
						try	{	
							this.lastParticipantCanDeregister = Boolean.parseBoolean(args[cur].toString());
						}
						catch(NumberFormatException e)	{
							System.out.println("Parsing lastAgentCanDeregister failed. \n " +e.getMessage());
						}
			}
		}
		
		addBehaviour(new RegistrationServiceBehaviour(this));
		addBehaviour(new DeregistrationServiceBehaviour(this));
		addBehaviour(new TableSizeResponseBehaviour(this));
		
		// HACK - wait a random time to avoid race conditions on ticks - better by 
		// external synchronization -> TODO
		addBehaviour(new WakerBehaviour(this, CentralRandom.nextInt(discussionTickerTime)) {
			private static final long serialVersionUID = 954665156698788790L;

			protected void onWake() {
				addBehaviour(new TickerBehaviour(getAgent(), discussionTickerTime) {
					private static final long serialVersionUID = -7538746921977978572L;
					protected void onTick() {
						addBehaviour(new DiscussionBehavior(getAgent()));
					}
				});
			}
		});
		
		// register at DF
		DFAgentDescription dfd = new DFAgentDescription();
		dfd.setName(getAID());
		ServiceDescription sd = new ServiceDescription();
		sd.setType(Constants.TABLE_REGISTRATION_SERVICE);
		sd.setName(getName());
		sd.setOwnership("TU Vienna");
		dfd.addServices(sd);
		try {
			DFService.register(this, dfd);
		}
		catch (FIPAException fe) {
			fe.printStackTrace();
			myLogger.log(Logger.SEVERE, "Agent "+getLocalName()+" - Cannot register with DF", fe);
			doDelete();
		}
		if (hasGui) {
		  // creates and shows the GUI
	      gui = new TableAgentGui(this);
	      gui.setVisible(true);
		}
		
		System.out.println("TableAgent "+getLocalName()+" created");
	}
	
	TableAgent getAgent() {
		return this;
	}
	protected void takeDown() {
		// Deregister from the DF
		try {
			DFService.deregister(this);
		}
		catch (FIPAException fe) {
			fe.printStackTrace();
		}
	}

	boolean hasParticipant(AID participantAID) {
		return participants.containsKey(participantAID);
	}
	
	void addParticipant(AID participantAID, InterestSet content) {
		// Sorry, this must go away - discussion only  at timed intervalls - you may create diff tableagent
		//addBehaviour(new DiscussionBehavior(getAgent()));
		
		//it is that the unchanged! discussion/topic(without the new one) 
		// is spread to everyone so that they can update their satisfaction 
		// correctly, since the messages inform them what they did discuss and 
		// not what they are going to
		this.participants.put(participantAID, content);
		updateGui();
	}
	
	boolean removeParticipant(AID participantAID) {
		// Sorry, this must go away - discussion only  at timed intervall - you may create diff tableagent
		//addBehaviour(new DiscussionBehavior(getAgent()));
		
		//as above
		boolean result = this.participants.remove(participantAID) != null;
		if (result) {
			updateGui();
		}
		return result;
	}
	
	Set<AID> getParticipants() {
		return participants.keySet();
	}

	int getParticipantsSize() {
		return participants.size();
	}
	
	InterestSet getInterestsOfParticipants() {
		return InterestSet.getCommonInterests(participants.values());
	}
	
    private void updateGui() {
    	if (hasGui) {
	    	StringBuffer buffer = new StringBuffer();
	    		for (AID aid:getParticipants()) {
	    			buffer.append(aid.getLocalName()+"\r\n");
	    		}
	    		
	        gui.setText(buffer.toString());
    	}
    }
	
}
